package JUC;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class Phone {
    /**
     * 同步方法，用于拨打电话
     * 该方法内部还会发送短信
     */
    public synchronized void callNumber() {
        System.out.println(Thread.currentThread().getName() + "\t打电话");
        sendSMS();
        System.out.println(Thread.currentThread().getName() + "\t打电话调用发短信成功");
    }

    /**
     * 同步方法，用于发送短信
     */
    public synchronized void sendSMS() {
        System.out.println(Thread.currentThread().getName() + "\t发短信");
    }

    // 创建一个可重入锁
    Lock lock = new ReentrantLock();

    /**
     * 使用可重入锁的拨打电话方法
     * 该方法内部还会发送短信
     */
    public void callNumber2() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "\t打电话");
            sendSMS2();
            System.out.println(Thread.currentThread().getName() + "\t打电话调用发短信成功");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    /**
     * 使用可重入锁的发送短信方法
     */
    public void sendSMS2() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "\t发短信");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

public class ReentrantDemo {

    /**
     * 程序入口
     * 演示了如何使用Phone类的方法，并展示了线程的执行顺序
     *
     * @param args 命令行参数
     * @throws InterruptedException 线程中断异常
     */
    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();
        // 创建并启动两个线程来拨打电话
        new Thread(() -> {
            phone.callNumber();
        }, "AAA").start();
        new Thread(() -> {
            phone.callNumber();
        }, "BBB").start();

        // 等待3秒，以便第一个两个线程执行完成
        Thread.sleep(3000);
        System.out.println();
        System.out.println();

        // 创建并启动另外两个线程来拨打电话，使用不同的锁机制
        new Thread(() -> {
            phone.callNumber2();
        }, "CCC").start();
        new Thread(() -> {
            phone.callNumber2();
        }, "DDD").start();
    }
}
